home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 1.iso / HENSA / MISC / GSTREAM / GSTREAM.ARC / c / gstream next >
Encoding:
Text File  |  1993-05-08  |  14.8 KB  |  675 lines

  1. /*************************************************************
  2. *                                                            *
  3. *    Module: gstream.c (part of gstream package)             *
  4. *                                                            *
  5. *   Version: 0.13                                            *
  6. *                                                            *
  7. *    Author: Simon Proven                                    *
  8. *                                                            *
  9. *     Owner:  Simon Proven 1993                             *
  10. *                                                            *
  11. *   Purpose: To provide a uniform means to access various    *
  12. *            types of device under RISC OS;                  *
  13. *                                                            *
  14. *      Uses: stdio, string, stdarg, stdlib, werr, flex       *
  15. *                                                            *                                                            *
  16. *   History:                                                 *
  17. *         <=0.09: prehistoric versions ;)                    *
  18. *           0.10: completed hooks for adding user-defined    *
  19. *                 stream types                               *
  20. *           0.11: added code to ensure errors closing        *
  21. *                 streams properly detected; added gserror() *
  22. *           0.12: fixed bug in gsread which read ~4Gbytes    *
  23. *                 when asked for less bytes than in the      *
  24. *                 stream's buffer                            *
  25. *           0.13: fixed a couple of bugs with internalr and  *
  26. *                 internalw streams                          *
  27. *                                                            *
  28. * Conditions:                                                *
  29. *             You can produce and sell executable code from  *
  30. *             this module freely.  The source code may be    *
  31. *             distributed freely, but not for profit.        *
  32. *                                                            *
  33. *             If you like these modules alot, and use them   *
  34. *             in your own programs, feel free to thank me    *
  35. *             with cash - 10 or more will get you a disc    *
  36. *             with the latest versions and new stream types. *
  37. *                                                            *
  38. * Disclaimer:                                                *
  39. *             This software is supplied in good faith, but I *
  40. *             cannot accept liability for any loss incurred  *
  41. *             through the use or inability to use any part   *
  42. *             of this software.                              *
  43. *                                                            *
  44. * How to contact me:                                         *
  45. *                                                            *
  46. * email:                      snail mail:                    *
  47. * sproven@cs.strath.ac.uk     Simon Proven,                  *
  48. *                             Castle Cottage,                *
  49. *                             Portencross,                   *
  50. *                             West Kilbride,                 *
  51. *                             KA23 9QA                       *
  52. *                             SCOTLAND                       *
  53. *                                                            *
  54. * Tel. (+44) 294 829 721                                     *
  55. *                                                            *
  56. *************************************************************/
  57.  
  58. #include <stdio.h>
  59. #include <string.h>
  60. #include <stdarg.h>
  61. #include <stdlib.h>
  62.  
  63. #include "werr.h"
  64. #include "gstream.h"
  65. #include "flex.h"
  66.  
  67. #define FALSE 0
  68. #define TRUE 1
  69.  
  70. /* this function tries to claim a flex block between maxsize
  71.  * and minsize bytes - it will return the number of bytes
  72.  * allocated, which may be zero
  73.  */
  74.  
  75. int gsgetbuf(flex_ptr anchor, size_t maxsize, size_t minsize)
  76. {
  77.     while(maxsize>=minsize)
  78.     {
  79.         if (flex_alloc(anchor,maxsize))
  80.         {
  81.             return maxsize;
  82.         }
  83.  
  84.         maxsize=((maxsize-minsize)>>1)+minsize;
  85.     }
  86.     return 0;
  87. }
  88.  
  89. /* opens an internal stream for writing to.  The stream's data is
  90.  * stored internally in a flex block.  Once the stream is closed,
  91.  * the stream can be re-opened using gsopeninternalr to read the
  92.  * information back again.  The stream's data can be discarded
  93.  * using gsdiscardinternal.  The data remains intact after using
  94.  * gsopeninternalr, so the stream can be closed and re-opened
  95.  * to read the data over and over again.
  96.  */
  97.  
  98. int gsopeninternalw(gstream *stream)
  99. {
  100.     stream->pos=0;
  101.     stream->size=0;
  102.     stream->type=INTERNAL_WRITE;
  103.  
  104.     if ((stream->bufsize=gsgetbuf(&(stream->buffer),gs_BUFSIZE,4))!=0)
  105.     {
  106.         stream->error=FALSE;
  107.         return TRUE;
  108.     }
  109.     else
  110.     {
  111.         werr(FALSE,"Not enough space");
  112.         return FALSE;
  113.     }
  114. }
  115.  
  116. int gsopeninternalr(gstream *stream)
  117. {
  118.     stream->type=INTERNAL_READ;
  119.     if (stream->buffer!=NULL)
  120.     {
  121.         stream->ungetsize=gsgetbuf(&(stream->ungetbuf),4,0);
  122.         stream->ungetpos=0;
  123.         stream->pos=0;
  124.         stream->error=FALSE;
  125.         return TRUE;
  126.     }
  127.     else
  128.         return FALSE;
  129. }
  130.  
  131. /* This function, when called for a write stream will cause all
  132.  * buffered data to be passed to the stream's write driver for
  133.  * sending to the host system.
  134.  *
  135.  * Currently, no action is performed on read streams - this may
  136.  * change in future versions (to be more like fflush)
  137.  */
  138.  
  139. int gsflush(gstream *stream)
  140. {
  141.     if (stream->error)
  142.         return -1;
  143.  
  144.     if (stream->bufsize!=0)
  145.     {
  146.         switch(stream->type)
  147.         {
  148.             case WRITE:
  149.                 if (stream->io.write(stream->buffer,stream->size,(void *) stream)<stream->size)
  150.                 {
  151.                     stream->size=0;
  152.                     return -1;
  153.                 }
  154.                 stream->size=0;
  155.                 return 0;
  156.  
  157.             case INTERNAL_WRITE:
  158.                 return 0;
  159.  
  160.             default:
  161.                 return -1;
  162.         }
  163.     }
  164.     else
  165.         return 0;
  166. }
  167.  
  168. /* this function will fill the buffer of a read stream from the
  169.  * host environment, but only when the buffer is empty.  At any
  170.  * other time, the buffer contents will not be changed.
  171.  *
  172.  * If, after calling this function, stream->size is zero then it
  173.  * may be assumed that there is no data remaining on the stream,
  174.  * unless the stream is unbuffered.
  175.  */
  176.  
  177. static void gsfillbuf(gstream *stream)
  178. {
  179.     if (stream->bufsize!=0)
  180.     {
  181.         switch(stream->type)
  182.         {
  183.             case READ:
  184.                 if (stream->pos==stream->size)
  185.                 {
  186.                     stream->size=stream->io.read(stream->buffer,stream->bufsize,(void *) stream);
  187.                     stream->pos=0;
  188.                 }
  189.                 break;
  190.             default:
  191.                 break;
  192.         }
  193.     }
  194. }
  195.  
  196. /* closes the given stream.  Returns 0 on success or non-zero on error */
  197.  
  198. int gsclose(gstream *stream)
  199. {
  200.     int flushedok,closedok;
  201.  
  202.     switch(stream->type)
  203.     {
  204.         case WRITE:
  205.             if (!stream->error) {
  206.                 flushedok=gsflush(stream);
  207.             }
  208.             closedok=stream->close(stream);
  209.             if (stream->bufsize!=0)
  210.                 flex_free(&(stream->buffer));
  211.             if (closedok && !flushedok || stream->error)
  212.                 return 0;
  213.             else
  214.                 return -1;
  215.             break;
  216.  
  217.         case READ:
  218.             closedok=stream->close(stream);
  219.             if (stream->ungetsize!=0)
  220.                 flex_free(&(stream->ungetbuf));
  221.             if (stream->bufsize!=0)
  222.                 flex_free(&(stream->buffer));
  223.             if (!closedok || stream->error)
  224.                 return -1;
  225.             else
  226.                 return 0;
  227.  
  228.         case INTERNAL_WRITE:
  229.             flex_extend(&(stream->buffer),stream->size);
  230.             if (stream->error)
  231.                 return -1;
  232.             else
  233.                 return 0;
  234.  
  235.         case INTERNAL_READ:
  236.             if (stream->ungetsize!=0)
  237.                 flex_free(&(stream->ungetbuf));
  238.             if (stream->error)
  239.                 return -1;
  240.             else
  241.                 return 0;
  242.     }
  243.     return 0;
  244. }
  245.  
  246. /* discards an internal stream's buffer contents */
  247.  
  248. void gsdiscardinternal(gstream *stream)
  249. {
  250.     flex_free(&(stream->buffer));
  251. }
  252.  
  253. /* reads a character from the given stream.  Returns EOF on end-of-file
  254.  * or error
  255.  */
  256.  
  257. int gsgetc(gstream *stream)
  258. {
  259.  
  260.     if (stream->error)
  261.         return EOF;
  262.  
  263.     if (stream->ungetpos==0)
  264.     {
  265.         switch (stream->type)
  266.         {
  267.             case READ:
  268.                 if (stream->bufsize!=0)
  269.                 {
  270.                     if (!(stream->pos<stream->size))
  271.                     {
  272.                         stream->size=0;
  273.                         stream->pos=0;
  274.                         gsfillbuf(stream);
  275.                     }
  276.  
  277.                     if (stream->pos<stream->size)
  278.                     {
  279.                         return(((char *)stream->buffer)[stream->pos++]);
  280.                     }
  281.                     else
  282.                         return(EOF);
  283.                 }
  284.                 else
  285.                 {
  286.                     char    c;
  287.                     if (gsread((void *) &c, 1, sizeof(char), stream)!=0)
  288.                     {
  289.                         return c;
  290.                     }
  291.                     else
  292.                         return EOF;
  293.                 }
  294.                 break;
  295.  
  296.             case INTERNAL_READ:
  297.                 if (stream->pos<stream->size)
  298.                     return(((char *)stream->buffer)[stream->pos++]);
  299.                 else
  300.                     return(EOF);
  301.             default:
  302.                 return(EOF);
  303.         }
  304.         return(EOF);
  305.     }
  306.     else
  307.     {
  308.         if (stream->ungetpos<(stream->ungetsize>>1))
  309.         {
  310.             flex_extend(&(stream->ungetbuf),stream->ungetsize>>1);
  311.             stream->ungetsize=stream->ungetsize>>1;
  312.         }
  313.         return ((char *)stream->ungetbuf)[--stream->ungetpos];
  314.     }
  315. }
  316.  
  317. /* puts a character to a given output stream.  Returns the value of c or
  318.  * EOF on error
  319.  */
  320.  
  321. int gsputc(int c, gstream *stream)
  322. {
  323.     if (stream->error)
  324.         return EOF;
  325.  
  326.     switch (stream->type)
  327.     {
  328.     case WRITE:
  329.         if (stream->bufsize!=0)
  330.         {
  331.             if (!(stream->size<stream->bufsize))
  332.             {
  333.                 if (gsflush(stream)<0)
  334.                     return EOF;
  335.             }
  336.             ((char *)stream->buffer)[stream->size++]=c;
  337.             return c;
  338.         }
  339.         else
  340.         {
  341.             char d;
  342.             d=c;
  343.             if (gswrite((void *) &d, 1, sizeof (char), stream)==1)
  344.                 return c;
  345.             else
  346.                 return EOF;
  347.         }
  348.     case INTERNAL_WRITE:
  349.         if (stream->size==stream->bufsize)
  350.         {
  351.             int increment=gs_BUFSIZE;
  352.             while(!flex_extend(&(stream->buffer),stream->bufsize+increment))
  353.             {
  354.                 increment=increment/2;
  355.             }
  356.             if (increment==0)
  357.             {
  358.                 werr(FALSE,"Not enough space");
  359.                 stream->error=TRUE;
  360.                 return EOF;
  361.             }
  362.             else
  363.                 stream->bufsize+=increment;
  364.         }
  365.         ((char *)stream->buffer)[stream->size++]=c;
  366.         return c;
  367.     default:
  368.         return EOF;
  369.     }    
  370. }
  371.  
  372. /* internal function which writes data to an internal stream - NEVER
  373.  * call this function yourself, always use gswrite
  374.  */
  375.  
  376. static size_t writeinternal(void *ptr, size_t nbytes, gstream *stream)
  377. {
  378.     if (stream->bufsize<(stream->size+nbytes))
  379.     {
  380.         if (!flex_extend(&(stream->buffer),stream->size+nbytes))
  381.         {
  382.             werr(FALSE,"Not enough space");
  383.             stream->error=TRUE;
  384.             return 0;
  385.         }
  386.         else
  387.             stream->bufsize=stream->size+nbytes;
  388.     }
  389.     memcpy((char *)stream->buffer+stream->size,(char *) ptr,nbytes);
  390.     stream->size+=nbytes;
  391.     return nbytes;
  392. }
  393.  
  394. /* writes stuff to a given stream.  identical in behaviour to stio's
  395.  * fwrite()
  396.  */
  397.  
  398. size_t gswrite(void *ptr, size_t size, size_t nmemb, gstream *stream)
  399. {    
  400.     size_t nbytes=size*nmemb;
  401.     int i;
  402.  
  403.     if (stream->error)
  404.         return 0;
  405.  
  406.     switch (stream->type)
  407.     {
  408.         case WRITE:
  409.             if (stream->bufsize!=0)
  410.             {
  411.                 if ((stream->bufsize-stream->size)>=nbytes)
  412.                 {
  413.                     memcpy((char *)stream->buffer+stream->size,(char *) ptr, nbytes);
  414.                     stream->size+=nbytes;
  415.                     return nbytes/size;
  416.                 }
  417.                 else
  418.                 {
  419.                     gsflush(stream);
  420.                     if ((stream->bufsize-stream->size)>=nbytes)
  421.                     {
  422.                         memcpy((char *)stream->buffer+stream->size,(char *) ptr, nbytes);
  423.                         stream->size+=nbytes;
  424.                         return nbytes/size;
  425.                     }
  426.                     else
  427.                     return(stream->io.write(ptr,nbytes,(void *)stream))/size;
  428.                 }
  429.             }
  430.             else
  431.                 return(stream->io.write(ptr,nbytes,(void *)stream))/size;
  432.         case INTERNAL_WRITE:
  433.             return(writeinternal(ptr,nbytes,stream))/size;
  434.         default:
  435.             for (i=0;i<nbytes;i++)
  436.                 if (gsputc(((char *)ptr)[i],stream)==EOF)
  437.                     return i/size;
  438.             return(nbytes)/size;
  439.     }
  440. }
  441.  
  442. /* internal function to read bytes from an internal read stream - NEVER
  443.  * call this yourself, always use gswrite()
  444.  */
  445.  
  446. static size_t readinternal(char *ptr, size_t nbytes, gstream *stream)
  447. {
  448.     if ((stream->size-stream->pos)>=nbytes)
  449.     {
  450.         memcpy(ptr,(char *)stream->buffer+stream->pos,nbytes);
  451.         stream->pos+=nbytes;
  452.         return nbytes;
  453.     }
  454.     else
  455.     {
  456.         int r=stream->size-stream->pos;
  457.         memcpy(ptr,(char *)stream->buffer+stream->pos,r);
  458.         stream->pos+=r;
  459.         return r;
  460.     }
  461. }
  462.  
  463. /* equivalent of fread() */
  464.  
  465. size_t gsread(void *ptr, size_t size, size_t nmemb, gstream *stream)
  466. {
  467.     size_t nbytes=size*nmemb;
  468.     int i,b,p;
  469.  
  470.     if (size==0 || nmemb==0 || stream->error)
  471.         return 0;
  472.  
  473.     if (stream->ungetpos==0)
  474.     {
  475.         switch (stream->type)
  476.         {
  477.             case READ:
  478.                 if (stream->bufsize==0)
  479.                     return (stream->io.read(ptr,nbytes,(void *) stream))/size;
  480.                 else if (stream->size==stream->pos)
  481.                 {
  482.                     gsfillbuf(stream);
  483.                     if (stream->size==stream->pos)
  484.                         return 0;
  485.                 }
  486.                 p=stream->pos;
  487.                 b=stream->size-p;
  488.                 if (nbytes>=b)
  489.                 {
  490.                     memcpy((char *)ptr,(char *)stream->buffer+p,b);
  491.                     stream->size=0;
  492.                     stream->pos=0;
  493.                     b+=stream->io.read((void *) ((char *)ptr+b),nbytes-b,(void *) stream);
  494.                     return b/size;
  495.                 }
  496.                 else
  497.                 {
  498.                     memcpy((char *)ptr,(char *)stream->buffer+p,nbytes);
  499.                     stream->pos+=nbytes;
  500.                     return nbytes/size;
  501.                 }
  502.                 break;
  503.  
  504.             case INTERNAL_READ:
  505.                 return(readinternal(ptr,nbytes,stream))/size;
  506.  
  507.             default:
  508.                 return 0;
  509.         }
  510.     }
  511.     else
  512.     {
  513.         int c;
  514.         for (i=0;i<nbytes;i++)
  515.         {
  516.             c=gsgetc(stream);
  517.             if (c==EOF)
  518.                 return i/size;
  519.             ((char *)ptr)[i]=c;
  520.         }
  521.         return nbytes/size;
  522.     }
  523. }
  524.  
  525. /* equivalent to fputs() */
  526.  
  527. int gsputs(const char *s, gstream *stream)
  528. {
  529.     size_t l=strlen(s);
  530.  
  531.     if (stream->error)
  532.         return EOF;
  533.  
  534.     /* this line untested - I've just swapped round the
  535.      * l and sizeof to remove a bug
  536.      */
  537.     if (l==gswrite((void *)s,sizeof(char),l,stream))
  538.         return 0;
  539.     else
  540.         return EOF;
  541. }
  542.  
  543. /* equivalent to fgets() */
  544.  
  545. char *gsgets(char *s, int n, gstream *stream)
  546. {
  547.     int l=0,c;
  548.  
  549.     if (stream->error)
  550.         return (NULL);
  551.  
  552.     while (l<(n-1) && (c=gsgetc(stream))!=EOF)
  553.     {
  554.         s[l++]=c;
  555.         if (c=='\n')
  556.         {
  557.             s[l]='\0';
  558.             return(s);
  559.         }
  560.     }
  561.  
  562.     if (l==0)
  563.         return(NULL);
  564.  
  565.     s[l]='\0';
  566.     return(s);
  567. }
  568.  
  569. /* equivalent of fprintf */
  570.  
  571. int gsprintf(gstream *stream, const char *format, ...)
  572. {
  573.     va_list p;
  574.     char buffer[gs_PRINTFMAX+1];
  575.     int l;
  576.     size_t w;
  577.  
  578.     if (stream->error)
  579.         return -1;
  580.  
  581.     va_start(p,format);
  582.  
  583.     l=vsprintf(buffer,format,p);
  584.  
  585.     va_end(p);
  586.  
  587.     /* swapped l and sizeof, untested, as in gsputs */
  588.     w=gswrite((void *)buffer,sizeof(char),l,stream);
  589.  
  590.     if (w==l)    
  591.         return(l);
  592.     else
  593.         return -1;
  594. }
  595.  
  596. /* equivalent of ungetc() */
  597.  
  598. int gsungetc(int c, gstream *stream)
  599. {
  600.  
  601.     if (c==EOF || stream->error)
  602.         return EOF;
  603.  
  604.  
  605.     /* first of all we check if the unget buffer is
  606.      * large enough and if not we extend it.  To do
  607.      * this we attempt to double its size and if that
  608.      * doesn't work then we try halving the increase
  609.      * in size until we get a buffer size that will
  610.      * fit into availabe memory.
  611.      */
  612.  
  613.     if (stream->ungetsize==stream->ungetpos)
  614.     {
  615.  
  616.         if (stream->ungetsize==0)
  617.         {
  618.             if (flex_alloc(&(stream->ungetbuf),4))
  619.             {
  620.                 stream->ungetsize=4;
  621.             }
  622.         }
  623.         else
  624.         {
  625.             int i=stream->ungetsize;
  626.             while (i!=0)
  627.             {
  628.                 if (flex_extend(&(stream->ungetbuf),i+stream->ungetsize))
  629.                 {
  630.                     stream->ungetsize+=i;
  631.                     break;
  632.                 }
  633.                 i=i>>1;
  634.             }
  635.         }
  636.     }
  637.  
  638.     /* now we have made our attempt to claim a buffer.  We
  639.      * now try to put the data in the unget buffer
  640.      */
  641.  
  642.     if (stream->ungetsize>(stream->ungetpos))
  643.     {
  644.         ((char *)stream->ungetbuf)[stream->ungetpos++]=c;
  645.     }
  646.     else
  647.     {
  648.         werr(FALSE,"gsungetc buffer full");
  649.         stream->error=TRUE;
  650.         return EOF;
  651.     }
  652.     return c;
  653. }
  654.  
  655. /* this function returns true if the next gsgetc on the
  656.  * stream will return EOF
  657.  */
  658.  
  659. int gseof(gstream *stream)
  660. {
  661.     int c;
  662.  
  663.     if ((c=gsgetc(stream))==EOF)
  664.         return TRUE;
  665.  
  666.     gsungetc(c,stream);
  667.     return FALSE;
  668. }
  669.  
  670. int gserror(gstream *stream)
  671. {
  672.     return stream->error;
  673. }
  674.  
  675.